/*
 * @Author: xiaosihan 
 * @Date: 2023-05-28 23:22:46 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2023-12-05 13:29:15
 */


import { DoubleSide, Event, EventListener, Group, Mesh, MeshStandardMaterial, Object3D } from "three";
import threeLoader from "three-base/threeLoader";
import threeUtils from "three-base/threeUtils";


// 模型基类
export default class MeshBase extends Group {
    constructor(url?: string) {
        super();
        url && this.loadModel(url);
    }

    // 模型对象
    static models: Map<string, Object3D> = new Map();

    // 模型是否在加载中
    static loadModeling: Map<string, boolean> = new Map();

    // 加载完成
    loaded = false;

    async loadModel(url: string) {
        if (!url) {
            this.clear();
            return;
        }

        this.loaded = false;

        // 没有模型并且没有加载任务时就去加载模型
        if (!MeshBase.models.get(url) && !MeshBase.loadModeling.get(url)) {
            MeshBase.loadModeling.set(url, true);
            const gltfObj = await threeLoader.gltfLoader.loadAsync(url);

            threeUtils.center(gltfObj.scene);
            threeUtils.alignBottom(gltfObj.scene);
            gltfObj.scene.updateMatrixWorld(true);

            // 应用全部变换
            gltfObj.scene.traverse(obj => {
                const mesh = obj as Mesh;
                mesh.position.set(0, 0, 0);
                mesh.rotation.set(0, 0, 0);
                mesh.scale.set(1, 1, 1);
                if (mesh.isMesh) {
                    mesh.geometry.applyMatrix4(mesh.matrixWorld);
                    (mesh.material as MeshStandardMaterial).side = DoubleSide;
                }
            });

            MeshBase.models.set(url, gltfObj.scene);
            MeshBase.loadModeling.set(url, false);
        }

        // 如果模型还在加载中, 就等待模型加载完成
        do {
            await new Promise(resolve => setTimeout(resolve, 200));
        } while (MeshBase.loadModeling.get(url));

        if (MeshBase.models.get(url)) {
            const mesh = MeshBase.models.get(url)!.clone();
            this.clear();
            this.add(mesh);
        }

        await new Promise(resolve => requestAnimationFrame(resolve));

        // 加载完成事件
        this.dispatchEvent({ type: "loaded" } as never);

        this.loaded = true;
    }

    // 超出绘制区域时不绘制
    frustumCulled = false;

    // 创建阴影
    castShadow: boolean = false;

    // 接受阴影
    receiveShadow: boolean = false;

    //遍历模型
    async traverseMesh(callback: (mesh: Mesh) => void) {

        while (!this.loaded) {
            await new Promise(resolve => setTimeout(resolve, 100));
        }

        this.traverse(obj => {
            const mesh = obj as Mesh;
            if (mesh.isMesh) {
                callback(mesh);
            }
        });
    }

    updateMatrix(): void {

        // 深度应用属性
        this.traverse(obj => {
            const mesh = obj as Mesh;
            if (mesh.isMesh) {
                mesh.frustumCulled = this.frustumCulled;
                mesh.castShadow = this.castShadow;
                mesh.receiveShadow = this.receiveShadow;
            }
        });

        super.updateMatrix();
    }

    // 添加事件
    // addEventListener<T extends string>(type: T, listener: EventListener<Event, T, this>): void {

    // }

}